/****************************************************************
 ** Product  :   HP Subscriber Network Application Policy
 ** Module   :   gr-manager
 ** Date: Apr 10, 2015               
 ** Author: Huang xiaoji
 ** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
 ** This software is the proprietary information of HP, Inc.  
 ** Use is subject to license terms.
 *****************************************************************
 ** Description:   
 **    Export Voltdb catalog jar
 **    
 ****************************************************************/
package com.hp.snap.gr.action.impl;

import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.exception.RESTfulException;
import com.hp.snap.gr.model.jaxb.ActionType;
import com.hp.snap.gr.model.jaxb.ExportVoltDBConfigurationActionType;
import com.hp.snap.gr.utils.Constants;
import com.hp.snap.gr.utils.RESTfulUtils;
import com.hp.snap.gr.utils.SSHTools;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Export Voltdb catalog jar. Through by call Restful interface to locate a database catalog.jar and scp the catalog.jar
 * to configured output directory, rename the catalog.jar to <dbname>.jar. 
 * Call the Restful interface are:
 *     /mgmt/databases : return databases information JSON string. 
 *     /mgmt/servers/<SERVER_ID> return server information JSON string. 
 *     /mgmt/deployments/<DEPLOYMENT_ID> return deployment information JSON string.
 * 
 * There are some parameters need to be configured: outputDirectory,vemHost,vemPort,vemUri,databases. 
 *     outputDirectory:The export destination directory. 
 *     vemHost: VoltDB database VEM host. 
 *     vemPort: VoltDB database VEM port. 
 *     vemUri:   VoltDB database VEM RESTful URI. 
 *     databases: database name to be exported.
 * 
 * <P>
 * Version: 3.3 <br>
 * Author: Huang xiaoji Date: Apr 10, 2015
 * 
 * </P>
 * 
 **/
public class ExportVoltDBConfigurationActionHandler extends AbstractActionHandler {
    private static final Logger logger = LoggerFactory.getLogger(ExportVoltDBConfigurationActionHandler.class);

    public SSHTools createSSHTools(String sshUser){
        return new SSHTools(sshUser, null);
    }
    @Override
    public void execute(ActionType action, Map<String, Object> actionPlanInputs, Map<String, Object> actionPlanOutputs) throws ActionException {
        if (action instanceof ExportVoltDBConfigurationActionType) {
            ExportVoltDBConfigurationActionType actionType = (ExportVoltDBConfigurationActionType) action;
            //get parameters from action
            String outputDirectory = actionType.getOutputDirectory();
            List<String> databases = actionType.getDatabase();

            Set<String> validDatabases = new HashSet<String>();
            if (databases != null && !databases.isEmpty()) {
                validDatabases.addAll(databases);
            }

            //get voltdb configuration
            String vemHost = actionType.getVemHost();
            int vemPort = Integer.parseInt(actionType.getVemPort());
            String vemUri = actionType.getVemUri();
            String vemSshUser = actionType.getVemSshUser();

            SSHTools ssh = createSSHTools(vemSshUser);

            if (logger.isDebugEnabled()) {
                logger.debug("using vem: {}@{}:{}{}", new Object[]{vemSshUser==null?"<GR USER>":vemSshUser, vemHost, vemPort, vemUri});
            }


            if (outputDirectory == null) {
                outputDirectory = Constants.USER_GR_WORK + File.separator +
                        "exportTarget" + File.separator + "VoltDB";
            }
            File output = new File(outputDirectory);
            output.mkdirs();

            String result = null;
            try {
                result = RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "/mgmt/databases",
                        Constants.HTTP_METHOD_GET, null);
            } catch (RESTfulException e) {
                throw new ActionException(e.getMessage(), e);
            }
            try {
                JSONArray array = new JSONObject(result).getJSONArray("Databases");
                for (int i = 0; i < array.length(); i++) {
                    JSONObject item = ((JSONObject) array.get(i)).getJSONObject("Database");

                    String name = item.getString("name");

                    if (!validDatabases.contains(name)) continue;
                    task taskobj = new task();
                    taskobj.vemHost = vemHost;
                    taskobj.deployment = item.getString("deployment");
                    taskobj.dbName = name;
                    taskobj.id = item.getLong("id");

                    String server = null;

                    JSONArray servers = item.getJSONArray("Servers");
                    for (int j = 0; j < servers.length(); j++) {
                        JSONObject obj = (JSONObject) servers.get(j);
                        if ("ONLINE".equals(obj.getString("status"))) {
                            server = obj.getString("server");
                            break;
                        }
                    }
                    if (server == null) {
                        throw new ActionException("no server configuration found");
                    }

                    try {
                        result = RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "/mgmt/servers/" + server,
                                Constants.HTTP_METHOD_GET, null);
                    } catch (RESTfulException e) {
                        throw new ActionException(e.getMessage(), e);
                    }
                    item = new JSONObject(result).getJSONObject("Server");
                    taskobj.dbHost = item.getString("host");
                    taskobj.dbSshKey = item.has("sshkey")?item.getString("sshkey"):null;
                    taskobj.dbSshUser = item.has("sshuser")?item.getString("sshuser"):null;

                    try {
                        result = RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "/mgmt/deployments/" + taskobj.deployment,
                                Constants.HTTP_METHOD_GET, null);
                    } catch (RESTfulException e) {
                        throw new ActionException(e.getMessage(), e);
                    }
                    item = new JSONObject(result).getJSONObject("Deployment");
                    taskobj.dbRoot = item.getString("voltroot");

                    backupVoltDB(ssh, output, taskobj);
                }
            } catch (JSONException e) {
                logger.error("parse json object failed, " + e.getMessage(), e);
                throw new ActionException(e.getMessage(), e);
            }

        }
    }

    private void backupVoltDB(SSHTools ssh, File output, task taskobj) throws ActionException {
        //SSH to vem host by default key
        String cmd = "scp " + (StringUtils.isEmpty(taskobj.dbSshKey)? "":("-i " + taskobj.dbSshKey)) + " " + (StringUtils.isEmpty(taskobj.dbSshUser)?"":(taskobj.dbSshUser + "@")) + taskobj.dbHost + ":" + taskobj.dbRoot + "/" + taskobj.id + "/catalog.jar /tmp/" + taskobj.dbName + ".jar";
        executeSSH(ssh, taskobj.vemHost, cmd);

        ssh.copyFromRemote(output.getAbsolutePath(), taskobj.vemHost, "/tmp/" + taskobj.dbName + ".jar");
    }

    private void executeSSH(SSHTools ssh, String host, String command) throws ActionException {
        try {
            if (logger.isTraceEnabled()) {
                logger.trace("going to execute command {} through {}", command, host);
            }
            String rs = ssh.cmd(host, command);
            if (logger.isTraceEnabled()) {
                logger.trace("execute on {} returned, result: {}", new Object[]{host, rs});
            }
            String[] vars = rs.split("\\|");
            if (!vars[0].trim().equals("0")) {
                throw new Exception("exit code is " + vars[0]);
            }
        } catch (Exception e) {
            logger.error("ssh command failed, " + e.getMessage(), e);
            throw new ActionException("execute command through SSH failed", e);
        }
    }

    class task {
        public String dbSshKey;
        public String dbSshUser;
        public String dbRoot;
        String vemHost, deployment, dbName, dbHost;
        long id;
    }
}
